package com.insight.spring;

import javafx.print.PaperSource;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/*
 * 1、负责造对象
 * 2、负责那对象
 * */
public class ApplicationContext {
    private HashMap<String,Object> ioc = new HashMap<>();
    private Map<String,BeanDefintion> beanDefintionMap = new  HashMap<>();

     public ApplicationContext(String packageName) throws Exception {
         initContext(packageName);
     }

     public void initContext(String packageName) throws Exception {
         //过滤是否拥有context的注解
         scanPackage(packageName).stream().filter(this::scanCreate).forEach(this::wrapper);
         beanDefintionMap.values().forEach(this::createBean);
     }

    private Object createBean(BeanDefintion beanDefintion) {
        String name = beanDefintion.getName();
        if (ioc.containsKey(name)){
            return ioc.get(name);
        }
        return  docreatBean(beanDefintion);
    }

    private Object docreatBean(BeanDefintion beanDefintion)  {
        Constructor<?> constructoe = beanDefintion.getConstructor();
        Object bean = null;
        try {
            bean = constructoe.newInstance();
            autowirtedBean(bean,beanDefintion);
            Method postConstructMethod = beanDefintion.getPostConstructMethod();
            if (postConstructMethod!=null){
                postConstructMethod.invoke(bean);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        ioc.put(beanDefintion.getName(),bean);
        return bean;
    }

    private void autowirtedBean(Object bean, BeanDefintion beanDefintion) throws IllegalAccessException {
        for (Field autowridFiled : beanDefintion.getAutowridFileds()) {
            autowridFiled.setAccessible(true);//属性可访问的
            autowridFiled.set(bean, getBean(autowridFiled.getType()));
        }
    }

    /*其他继承重写可以实现什么样的类可以支持加载的*/
     protected boolean scanCreate(Class<?> type){
         return type.isAnnotationPresent(Component.class);
     }
     protected BeanDefintion wrapper(Class<?> type){
         BeanDefintion beanDefintion = new BeanDefintion(type);
         if (beanDefintionMap.containsKey(beanDefintion.getName())){
             throw new RuntimeException("bean名字重复");
         }
         beanDefintionMap.put(beanDefintion.getName(), beanDefintion);
         return beanDefintion;
     }
     protected void createBean(){

     }
     private List<Class<?>> scanPackage(String packageName) throws IOException, URISyntaxException {
         ArrayList<Class<?>> classList = new ArrayList<>();
         URL resource = this.getClass().getClassLoader().getResource(packageName.replace(".", File.separator));
         Path path = Paths.get(resource.toURI());
         Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
             @Override
             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                 Path absolutePath = file.toAbsolutePath();
                 if (absolutePath.toString().contains(".class")){
                     String replaceStr = absolutePath.toString().replace(File.separator, ".");
                     int packageIndex = replaceStr.indexOf(packageName);
                     String className = replaceStr.substring(packageIndex,replaceStr.length()-".class".length());
                     try {
                         classList.add(Class.forName(className));
                     } catch (ClassNotFoundException e) {
                         e.printStackTrace();
                     }
                 }
                 return FileVisitResult.CONTINUE;
             }
         });
         return classList;

     }
     public Object getBean(String name){
         if (name == null){
             return  null;
         }
         Object bean = this.ioc.get(name);
         if (bean !=null){
             return bean;
         }
         if (beanDefintionMap.containsKey(name)){
             return createBean(beanDefintionMap.get(name));
         }
         return null;
     }

    public <T> T getBean(Class<T> beanType){
        String beanName =
        this.beanDefintionMap.values().stream().filter(bd->beanType.isAssignableFrom(bd.getBeanType()))
                .map(BeanDefintion::getName).
                findFirst().orElse(null);
        return  (T) getBean(beanName);
    }

    public <T> List<T> getBeans(Class<T>  beanType){
         return   this.beanDefintionMap.values().stream().filter(bd->beanType.isAssignableFrom(bd.getBeanType()))
                .map(BeanDefintion::getName).map(this::getBean) .map(bean->(T)bean).collect(Collectors.toList());
    }



}
